import axios from "axios";
import process from "process";
import {expect} from "chai";
import {
    getCertificateBasePath,
    getCustomMavenSettingCertificate,
    getDockerAcrEnterpriseCertificate,
    getDockerAcrPersonalCertificate,
    getDockerCustomPersonalCertificate,
    getMavenPrivateRepoCertificate,
    getNewCustomMavenSettingCertificate,
    getNewMavenPrivateRepoCertificate,
    getNpmPrivateRepoCertificate,
    getOssRamStsCertificate,
    getOssRamServiceConnectionCertificate,
    getRamStsCertificate,
    getCodeupCertificate,
    OssBucketType,
    getKubernetesCertificate, getJenkinsCertificate, getNugetPrivateRepoCertificate,
    getCertificate
} from '../../src/certificate/certificate'
import * as sinon from 'sinon';

describe('getCertificateBasePath', () => {
    it('should throw an error if FLOW_OPENAPI_URL is empty', () => {
        delete process.env['FLOW_OPENAPI_URL'];
        expect(getCertificateBasePath).to.throw('missing \'FLOW_OPENAPI_URL\' env variables');
    });

    it('should return the correct certificate base path', () => {
        process.env['FLOW_OPENAPI_URL'] = 'https://flow.aliyun.com';
        const expectedPath = 'https://flow.aliyun.com/openapi/certificates';
        expect(getCertificateBasePath()).to.equal(expectedPath);
    });
});


let axiosRequestStub: sinon.SinonStub;

describe('getCertificate', () => {

    beforeEach(() => {
        process.env.FLOW_OPENAPI_URL = 'https://flow-test.aliyun.com'
    })

    it('getMavenPrivateRepoCertificate should return certificate record', async () => {
        const repoSettings = "<xml>xxxx</xml>>";
        const response = {
            data: [{
                "key": "REPO_SETTINGS",
                "value": repoSettings
            }],
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getMavenPrivateRepoCertificate(flowJobToken)
        expect(result["REPO_SETTINGS"]).to.equal(repoSettings)
        const params = {
            "flowJobToken": flowJobToken
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/mavenPrivateRepo",
            params: params
        })
    })

    it('getCustomMavenSettingCertificate should return certificate record', async () => {
        const repoSettings = "<xml>xxxx</xml>>";
        const response = {
            data: [{
                "key": "CUSTOM_MAVEN_SETTING_XML",
                "value": repoSettings
            }],
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getCustomMavenSettingCertificate(flowJobToken)
        expect(result["CUSTOM_MAVEN_SETTING_XML"]).to.equal(repoSettings)
        const params = {
            "flowJobToken": flowJobToken
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/mavenCustomSetting",
            params: params
        })
    })

    it('getNewMavenPrivateRepoCertificate should return certificate content', async () => {
        const repoSettings = "<xml>xxxx</xml>>";
        const response = {
            data: {
                "mavenSettingsContent": repoSettings
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getNewMavenPrivateRepoCertificate(flowJobToken)
        expect(result.mavenSettingsContent).to.equal(repoSettings)
        const params = {
            "flowJobToken": flowJobToken
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/newMavenPrivateRepo",
            params: params
        })
    })

    it('getNewCustomMavenSettingCertificate should return certificate content', async () => {
        const repoSettings = "<xml>xxxx</xml>>";
        const response = {
            data: {
                "mavenSettingsContent": repoSettings
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getNewCustomMavenSettingCertificate(flowJobToken)
        expect(result.mavenSettingsContent).to.equal(repoSettings)
        const params = {
            "flowJobToken": flowJobToken
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/newMavenCustomSetting",
            params: params
        })
    })

    it('getNpmPrivateRepoCertificate should return certificate record', async () => {
        const repoConfigContent = "registry:http://xxx/npm/";
        const response = {
            data: {
                "npmRepoConfigContent": repoConfigContent
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getNpmPrivateRepoCertificate(flowJobToken)
        expect(result.npmRepoConfigContent).to.equal(repoConfigContent)
        const params = {
            "flowJobToken": flowJobToken
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/npmPrivateRepo",
            params: params
        })
    })

    it('getDockerAcrEnterpriseCertificate should return certificate record', async () => {
        const response = {
            data: {
                "username": "root",
                "password": "123456"
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getDockerAcrEnterpriseCertificate(flowJobToken, "1234", "cn-beijing", "aaaa")
        expect(result.password).to.equal("123456")

        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234",
            aliyunRegion: "cn-beijing",
            instanceName: "aaaa"
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/dockerAcrEnterprise",
            params: params
        })
    })

    it('getDockerAcrPersonalCertificate should return certificate record', async () => {
        const response = {
            data: {
                "username": "root",
                "password": "123456"
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getDockerAcrPersonalCertificate(flowJobToken, "1234", "cn-beijing")
        expect(result.password).to.equal("123456")
        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234",
            aliyunRegion: "cn-beijing"
        }

        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/dockerAcrPersonal",
            params: params
        })
    })

    it('getDockerCustomPersonalCertificate should return certificate record', async () => {
        const response = {
            data: {
                "username": "root",
                "password": "123456"
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getDockerCustomPersonalCertificate(flowJobToken, "1234")
        expect(result.password).to.equal("123456")
        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234"
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/dockerCustomPersonal",
            params: params
        })
    })


    it('getRamStsCertificateCertificate should return certificate record', async () => {
        const response = {
            data: {
                "accessKeyId": "ACCESS_KEY_ID",
                "accessKeySecret": "ACCESS_KEY_SECRET",
                "securityToken": "SECURITY_TOKEN",
                "accountId": "ACCOUNT_ID"
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getRamStsCertificate(flowJobToken, "1234")
        expect(result.accessKeySecret).to.equal("ACCESS_KEY_SECRET")

        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234"
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/ramSts",
            params: params
        })
    })

    it('getOSSRamStsCertificateCertificate should return certificate record', async () => {
        const response = {
            data: {
                "accessKeyId": "ACCESS_KEY_ID",
                "accessKeySecret": "ACCESS_KEY_SECRET",
                "securityToken": "SECURITY_TOKEN",
                "bucketName": "bucketName",
                "ossPathPrefix": "ossPathPrefix"

            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getOssRamStsCertificate(flowJobToken, OssBucketType.ARTIFACT)
        expect(result.bucketName).to.equal("bucketName")

        const params = {
            "flowJobToken": flowJobToken, ossBucketType: OssBucketType.ARTIFACT
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/ossRamSts",
            params: params
        })
    })

    it('getOSSServiceConnectionCertificate should return certificate record', async () => {
        const response = {
            data: {
                "accessKeyId": "ACCESS_KEY_ID",
                "accessKeySecret": "ACCESS_KEY_SECRET",
                "securityToken": "SECURITY_TOKEN",
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getOssRamServiceConnectionCertificate(flowJobToken, "1234")
        expect(result.securityToken).to.equal("SECURITY_TOKEN")
        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234"
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/ossRamSc",
            params: params
        })
    })

    it('getCodeupCertificate should return certificate record', async () => {
        const response = {
            data: {
                "host": "codeup.aliyun.com",
                "username": "root",
                "password": "123456"
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const result = await getCodeupCertificate(flowJobToken, "1234")
        expect(result.password).to.equal("123456")
        const params = {
            "flowJobToken": flowJobToken,
            serviceConnectionId: "1234"
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/codeup",
            params: params
        })
    })

    it('should return ack KubernetesCertificate record', async () => {
        const response = {
            data: {
                "type": "ack",
                "kubeConfig": "",
                "miranaProxy": "http://proxy.example.com",
                "jwtToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
            }
        };

        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);
        axiosRequestStub.resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const serviceConnectionId = "1234";

        const result = await getKubernetesCertificate(flowJobToken, serviceConnectionId);
        expect(result.jwtToken).to.equal(response.data.jwtToken);

        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/kubernetes",
            params: {
                flowJobToken,
                serviceConnectionId
            }
        });
    });

    it('getJenkinsCertificate should return certificate record', async () => {
        const response = {
            data: {
                jenkinsMaster: 'http://localhost:8080',
                jenkinsUsername: 'root',
                jenkinsPassword: '123456'
            }
        }
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response)

        const flowJobToken =
            '3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D'
        const result = await getJenkinsCertificate(flowJobToken, '1234')
        expect(result.jenkinsMaster).to.equal('http://localhost:8080')
        expect(result.jenkinsUsername).to.equal('root')
        expect(result.jenkinsPassword).to.equal('123456')

        const params = {
            flowJobToken: flowJobToken,
            serviceConnectionId: '1234'
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: 'https://flow-test.aliyun.com/openapi/certificates/jenkins',
            params: params
        })
    })

    it('getNugetPrivateRepoCertificate should return certificate record', async () => {
        const mockedNugetRepoConfigContent = "<Config>xxxx</Config>>";
        const repoIds = 'repo1,repo2'
        const response = {
            data: {
                "nugetRepoConfigContent": mockedNugetRepoConfigContent
            }
        };
        axiosRequestStub = sinon.stub(axios, 'request').resolves(response);

        const flowJobToken = "3jNKknxWk%2FpG96F0EoaaessTgvTY9DzQFOpmqQgUs37f%2F9jmewaFLIr%2FrxhZ3qaZPEtd7DCWKh4vU98X4QV2ug%3D%3D";
        const serviceConnectionId = "1234";
        const result = await getNugetPrivateRepoCertificate(flowJobToken, serviceConnectionId, repoIds)
        expect(result.nugetRepoConfigContent).to.equal(mockedNugetRepoConfigContent)
        const params = {
            "flowJobToken": flowJobToken,
            "serviceConnectionId": serviceConnectionId,
            "repoIds": repoIds
        }
        sinon.assert.calledOnceWithExactly(axiosRequestStub, {
            method: 'GET',
            url: "https://flow-test.aliyun.com/openapi/certificates/nugetPrivateRepo",
            params: params
        })
    })

    afterEach(() => {
        process.env.FLOW_OPENAPI_URL = ''
        sinon.restore();
    })
})

describe('getCertificate', () => {
    let axiosRequestStub: sinon.SinonStub;

    beforeEach(() => {
        axiosRequestStub = sinon.stub(axios, 'request');
    });

    afterEach(() => {
        axiosRequestStub.restore();
    });

    it('should return data on successful request', async () => {
        const mockData = { key: 'value' };
        axiosRequestStub.resolves({ data: mockData });

        const result = await getCertificate<{ key: string }>('http://example.com', { param: 'value' });
        expect(result).to.deep.equal(mockData);
    });

    it('should retry on failure and succeed on subsequent attempt', async () => {
        const mockData = { key: 'value' };
        axiosRequestStub.onCall(0).rejects(new Error('Network Error'));
        axiosRequestStub.onCall(1).resolves({ data: mockData });

        const result = await getCertificate<{ key: string }>('http://example.com', { param: 'value' });
        expect(result).to.deep.equal(mockData);
        expect(axiosRequestStub.callCount).to.equal(2);
    });

    it('should throw an error after reaching max retries', async () => {
        axiosRequestStub.rejects(new Error('Network Error'));

        try {
            await getCertificate<{ key: string }>('http://example.com', { param: 'value' });
            expect.fail('Expected to throw an error');
        } catch (error) {
            // @ts-ignore
            expect(error.message).to.include('Get http://example.com failed, abort request.');
        }
        expect(axiosRequestStub.callCount).to.equal(3);
    });
});